﻿//-----------------------------------------------------------------------
// <copyright file="MinMax.cs" company="Nathan Miller">
// Copyright (c) Nathan Miller.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
//-----------------------------------------------------------------------
namespace TileTool.Common.Math
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// Class which contains a min and max value and updates it
    /// as values are added to the range.
    /// 
    /// The generic type must be comparable via the default
    /// comparator Comparer<T>.Default.Compare().
    /// 
    /// </summary>
    /// <typeparam name="T">Type to track min and max values for.</typeparam>
    public class MinMax<T>
    {
        #region Constructor

        /// <summary>
        /// Constructor which takes default min and max values.
        /// </summary>
        /// <param name="min">Current min value.</param>
        /// <param name="max">Current max value.</param>
        public MinMax(T min, T max)
        {
            this.Min = min;
            this.Max = max;
        }

        #endregion

        #region Properties - Public

        /// <summary>
        /// Gets the current min value.
        /// </summary>
        public T Min
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the current max value.
        /// </summary>
        public T Max
        {
            get;
            private set;
        }

        #endregion

        #region Methods - Public

        /// <summary>
        /// Add a value to the range.
        /// </summary>
        /// <param name="value">Value to add.</param>
        public void Add(T value)
        {
            int res = Comparer<T>.Default.Compare(value, this.Min);
            if (res < 0)
            {
                this.Min = value;
            }

            res = Comparer<T>.Default.Compare(value, this.Max);
            if (res > 0)
            {
                this.Max = value;
            }
        }

        /// <summary>
        /// Tests if the passed range overlaps this one.
        /// </summary>
        /// <param name="other">Other min max range to test.</param>
        /// <returns>Returns true if the ranges overlap.</returns>
        public bool Overlap(MinMax<T> other)
        {
            // We overlap when our min value is less than the other max
            // and when our max is greater than the other min.
            int minCompare = Comparer<T>.Default.Compare(this.Min, other.Max);
            int maxCompare = Comparer<T>.Default.Compare(this.Max, other.Min);

            return (minCompare <= 0) && (maxCompare >= 0);
        }

        /// <summary>
        /// Clamp the passed value to our range.
        /// </summary>
        /// <param name="value">Value to clamp.</param>
        /// <returns>Returns the value clamped to the min / max range.</returns>
        public T Clamp(T value)
        {
            int minCompare = Comparer<T>.Default.Compare(value, this.Min);
            if (minCompare < 0)
            {
                return this.Min;
            }

            int maxCompare = Comparer<T>.Default.Compare(value, this.Max);
            if (maxCompare > 0)
            {
                return this.Max;
            }

            return value;
        }

        #endregion
    }
}
